package com.kuangstudy.service.zset;

import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * @author 飞哥
 * @Title: 学相伴出品
 * @Description: 我们有一个学习网站：https://www.kuangstudy.com
 * @date 2021/6/7 14:54
 */
@Service
@Log4j2
public class WeiboxTaskService {

    @Autowired
    private RedisTemplate redisTemplate;

    @PostConstruct
    public void init() {
        log.info("模拟微博排行榜单-启动初始化开始....");
        // 1:定时5秒钟，模拟微博的热度刷新(比如：模拟点赞，收藏，评论数热度值的更新)
        new Thread(() -> this.refreshDataHour()).start();
        // 2：定时1个小时合并天、周、月的排行榜。
        new Thread(() -> this.refreshAllData()).start();

    }

    // 定时五秒去刷新一次，模拟微博的点赞，评论，收藏数
    public void refreshAllData() {
        while (true) {
            // 刷新天
            this.refreshDay();
            // 刷新周
            this.refreshWeek();
            // 刷新月
            this.refreshMonth();
            // 如果是在分布式系统中：建议使用xxljob来实现定时刷新
            try {
                Thread.sleep(1000 * 60 * 60);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }

    // 定时五秒去刷新一次，模拟微博的点赞，评论，收藏数
    public void refreshDataHour() {
        while (true) {
            this.refreshHour();
            // 如果是在分布式系统中：建议使用xxljob来实现定时刷新
            try {
                Thread.sleep(5000);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }

    /*
        采用26个字母实现排行，随机为每篇文章生成一个随机的score,并且模拟点赞，收藏和评论
     */
    public void refreshHour() {
        // 1: 计算当前的小时key
        long hour = System.currentTimeMillis() / (1000 * 60 * 60);
        // 2: 把近一个月的数据放入到zset中
        Random random = new Random();
        for (int i = 1; i <= 26; i++) {
            this.redisTemplate.opsForZSet().incrementScore("rank:hour:" + hour, String.valueOf((char) (96 + i)), random.nextInt(10));
        }
    }

    /*
      刷新近30天的统计数
   */
    public void refreshMonth() {
        // 1: 计算当前的小时key
        long hour = System.currentTimeMillis() / (1000 * 60 * 60);
        List<String> otherkeys = new ArrayList<>();
        // 2 ：算出近24小时内的key
        for (int i = 0; i < 24 * 30 - 1; i++) {
            String key = "rank:hour:" + (hour - i);
            otherkeys.add(key);
        }
        // 把当前的小时时间的key,并且往后推24*30-1小时，共计24*30小时，并求出并集存入"rank:month"中
        this.redisTemplate.opsForZSet().unionAndStore("rank:hour:" + hour, otherkeys, "rank:month");
        log.info("月刷新合并完成...");
    }

    /*
       刷新近7天的统计数
    */
    public void refreshWeek() {
        // 1: 计算当前的小时key
        long hour = System.currentTimeMillis() / (1000 * 60 * 60);
        List<String> otherkeys = new ArrayList<>();
        // 2 ：算出近24小时内的key
        for (int i = 0; i < 24 * 7 - 1; i++) {
            String key = "rank:hour:" + (hour - i);
            otherkeys.add(key);
        }
        // 把当前的小时时间的key,并且往后推24*7-1小时，共计24*7小时，并求出并集存入"rank:week"中
        this.redisTemplate.opsForZSet().unionAndStore("rank:hour:" + hour, otherkeys, "rank:week");
        log.info("天刷新合并完成...");
    }

    /*
      刷新当天的统计数据
   */
    public void refreshDay() {
        // 1: 计算当前的小时key
        long hour = System.currentTimeMillis() / (1000 * 60 * 60);
        List<String> otherkeys = new ArrayList<>();
        // 2 ：算出近24小时内的key
        for (int i = 1; i <= 23; i++) {
            String key = "rank:hour:" + (hour - i);
            otherkeys.add(key);
        }
        //3：把当前的时间key和后推的23小时的key，求出并集存入到rank:day集合中
        // 这里就是利用redis的zunionstore把当前时间和24小时之前时间
        this.redisTemplate.opsForZSet().unionAndStore("rank:hour:" + hour, otherkeys, "rank:day");

        //4：设置当天的key，40天过期，不然历史数据浪费内存
        for (int i = 0; i < 24; i++) {
            String key = "rank:hour:" + (hour - i);
            this.redisTemplate.expire(key, 40, TimeUnit.DAYS);
        }

        log.info("天刷新合并完成...");
    }

}
